DefMod

Contents

Contents
Introduction
Module interface files
Syntax
Semantics
The ByteWide file
APCS to SWI
Implementation notes
The grammar
An example file
Future possibilities
Revision history

Introduction

DefMod is the tool for generating all sorts of different files from module interface files. A module interface file (with the conventional file extension .swi) contains enough information to specify, in a language-independent form, the interface to a RISC OS module. This can be used to generate suitable files to enable the module to be called from any high-level language. The whole of OSLib (apart from "types.h" and "macros.h") is derived from a set of modules interface files and these DefMod options:

-asmtype <asmtype>
selects the type of assembler program to be used. Possible values for <asmtype> are objasm (ObjAsm running on RISC OS) which is the default value, asasmaof (AsAsm with AOF output), asasmelf (AsAsm with ELF output), gccaof (GCCSDK 3.4 AOF), armasm (ArmAsm) and gccelf (GCCSDK 4 ELF).
-float-abi <float ABI>
marks object files marks object files which float-abi is in use. Possible values for <float ABI> are soft (floating point values are passed in integer registers and/or the stack, no FP instructions are used), hard (floating point values are passed in FP registers, FP instructions can be used).
-o
produces a directory containing ObjAsm or GAS source files defining the SWI veneers as separate files; object files corresponding to them; and a text file ViaFile which may be ued as input to LibFile to combine them into a library
-h
produces a commented C header file defining all the constants as macros, all the types as typedef's and all the SWI's as function prototypes
-hdr
produces a commented assembler header file defining all the constants as symbols, all the types and structure offsets as symbols, and all the SWI's as symbols
-chelp
produces a SrcEdit-compatible help file for the C interface
-cstrong
produces a StrongHelp-compatible help file for the C interface
-vapi
produces vapi files for the Vala programming language.
-vapideps
produces deps files which contain the dependencies between different vapi files used by the Vala programming language.
-vapih
produces (parts of) C headerfiles for use with the Vala programming language.

For other options, see DefMod -help.

DefMod can be most useful when used at the start of a project: it can be used to specify the whole of the module interface before any of the module is written. The header files it generates can then be used during actual development of the module. This means that all the resources used by the module are defined in one place only---the module interface file---which will then be the master for things like SWI numbers, command numbers, error numbers, structures and SWI calling conventions for development of the module itself and its clients.

Module interface files

The format of a module interface file is described completely by the grammar which accompanies this document. A brief outline follows.

Syntax

There is no separate lexical analyser: the grammar goes all the way to the individual character level. A consequence of this is that a convention is needed to keep reserved words grammatically distinct from user-defined identifiers: since the grammar should be case-insensitive but case-preserving (at least in the area of SWI names), case is not used as the mechanism: instead, reserved words start with a full stop.

There are some conventions (metarules, really) used in the grammar:

Case in the interface file is important, because it is used to decide where to break up a name. SWI names must always be specified exactly in the form used by the system. All symbols should begin with the module prefix, or Error_, Event_, Message_, Service_, UpCall_ or else should end in V (for vectors).

There is an example module interface file for the ColourPicker module (see An example file).

A file consists of a series of declarations. There are 6 different types of declaration. Each type may occur as many times as necessary. The way in which commas and semi-colons are used may be a bit confusing: they are separators, not terminators as in C, so, for example, a CONST section might be

      CONST
         a = T: 0,
         b = T: 1,
         c = T: 2

and there is no comma at the end. If there were multiple sections in a file, then semi-colons appear between them, but not at the end, like this:

      CONST section; TYPE section; SWI section; another CONST section

This will all be familiar to users of Pascal or ALGOL.

A type_defn with no type defines an "abstract type". A structure type may end with ..., in which case various extra macros will be generated. An abstract type name is often a single letter (as in wimp_w, os_f).

A SWI number may be followed with a textual description or with a star (in which case a description will be made up). A reason code will be created (instead of a standalone SWI) when the SWI number does not contain a description (nor a star) and there is at least one constant register definition with a non-empty text description or star. When there is more than one constant register definition with a non-empty test description or star, the first one mentioned will be considered to be the reason code and its description (or the made one up in case of a star) will be used to describe this SWI entry. A warning is given when the SWI entry does not contain any description nor star, or when the SWI number and one or more constant register definitions do have a textual description (and no star).

One output register may be marked with a pling, in which case the non-x form will return it as its result.

Constants may be provided in decimal, hex (& or 0x) or binary (% or 0b) or as a character value of up to 4 characters (e g, "TASK").

Semantics

The TITLE section

This gives the module a title. It is used to produce a comment in output, and should be the same as the module prefix. There can only be one of these.

The AUTHOR section

This gives a string which appears in the output, also as a comment. There can only be one of these.

The NEEDS section

Contains the names of other module interfaces on which this one depends. This does not cause the named module interface file to be read by DefMod, but may cause it to emit some kind of include directive in the output.

The CONST section

Contains constant definitions. Constants may be of a built-in type or one defined in this or another interface file.

The TYPE section

Defines types. Types are built-in types (Int, Short, Byte, Char, Bits, Bool, String, Asm, Data) or derived types. They are classified into "register", "non-register fixed-length" and "non-register variable-length" types. The register types are: Int, Short, Byte, Char, Bits, Bool, Asm, Ref anything, and unions of these; non-register fixed-length types are: arrays, structures containing no ..., and unions of anything so far; and non-register variable-length types are: String, Data, Struct with ..., and unions not so far mentioned.

In fact, not all the checking that would be desirable can be done, since DefMod deals with only one file at a time.

The reasons for the distinctions are (a) that only register types may be arguments of SWI calls; (b) special treatment is given to structures whose last component is a repeating fixed-length element, in the form of extra macros to deal with objects of those types.

The built-in types must be provided by the target language. For C, they are in the file "types.h", included by all headers.

Within unions only, the type Void is allowed, signifying that the union may be "empty".

The SWI section

Contains the registers needed on input, and produced on output, by each SWI the module defines. For details of how the algorithm for producing the APCS veneers works, see the section APCS to SWI. Registers may be specified as

The two forms

      Rx = .Ref Thing: thing

(Rx contains a pointer to a Thing called thing) and

      Rx -> Thing: thing

(Rx points to a Thing called thing) are closely related: the difference is one of intent only. The second form is used when the argument must be valid on input; so the SWI is accepting a value of type Thing as an argument, represented by a pointer to it ("input" arguments); the first form is used when the value is not present, but the SWI will fill it in ("output" arguments). (A third form of syntax could be provided for "update" arguments, but this has not been done.) In C, the first form generates an ordinary pointer, the second a pointer-to-constant.

The .swi file contains the whole of the module interface. This includes modules SWI's, structures and constants, as expected; but also other resources defined by the module: service calls, vectors, error numbers, upcalls, Wimp messages (numbers and structures), events, etc. The vector, upcall and service entries are useful when making the call, typically done within the module rather than by a client. Even so, they are part of the module interface and logically belong in the interface file.

We assume that a non-x form will only be called in user mode: therefore, no attempt is made to save LR over the SWI instruction.

The output arguments are not written if a SWI fails.

The case of PSR flags being required on input is not supported. The only two calls where this is necessary are RemV and CnpV, which are obsolescent anyway.

If a call has no output arguments, and exactly one input argument which is a pointer to an unnamed structure with no ... part, then the structure elements are passed in in separate registers. See Wimp_SetIconState for an example.

If a SWI call happens also to be APCS-compliant, this information can be passed to the compiler, which can then generate an inline SWI instruction, by using the __swi() directive provided with ARM C release 5. To disable this feature for use with earlier or non-ARM compilers (e.g., CFront) requires a header file containing the lines

      #ifdef  __swi
      #undef  __swi
      #define __swi(x) extern
      #endif

and the -D__swi compiler flag must be provided when using the header files. "Types.h" contains these lines.

The ByteWide file

In order to generate correct code, the width of all types used for output needs to be known. (Bytes must be stored with byte-store instructions.) In order to avoid having to read all NEEDS files, a separate list of byte-wide types must be provided. All other undefined types will be assumed to be register-wide, and a warning will be given. For OSLib, the byte-wide file is

      Font_F
      OS_Action
      OS_F
      OS_GCOL
      OS_Tint
      Wimp_Colour

There is no support for 2-byte quantities on output.

APCS to SWI

"APCS to SWI" is what DefMod does when generating library files: it generates a veneer that assumes the C function has been called via APCS (in other words, the first 4 arguments are in R0, ..., R3, and the rest are on the stack), and sets the registers up as they should be for calling the SWI; that calls the SWI; and that then puts all the registers back where they should be for the APCS-conformant exit conditions (result in R0, other output values written to the addresses supplied on input).

This section describes how it does it.

Notation

To simplify the discussion, we refer to the registers by 2 sets of names. Considered as input to and output from the SWI, we call them R0, ..., R9; considered as APCS entry registers, we call them A1, ..., A4 (the "argument registers") and V1, ..., V6 (the "variable registers"); also, when considered as the APCS result register, we call R0 just R. The remaining 6 registers are always called SL, FP, IP, SP, LR, PC.

Let i be the number of registers provided on input, and let the registers be I0, ..., Ii-1; let o be the number of results required, and let the registers be O0, ..., Oo-1; let c be the number of registers corrupted, and let the registers be C0, ..., Cc-1; let k be the number of registers to be loaded with constants, and let the registers be K0, ..., Kk-1. Also, let f be 1 if flags are to be supplied on output, 0 if not.

Recall that on entry

The 1st argument is in R0 (A1)
The 2nd argument is in R1 (A2)
The 3rd argument is in R2 (A3)
The 4th argument is in R3 (A4)
The ith argument is at [SP, #4*(i-4)] (i > 4)

So on entry, RI0, ..., RIi-1 must be copied from the 0th, ..., (i-1)th arguments and RK0, ..., RKk-1 must be set to their values; and on exit,

If V is set R must be set to an error block pointer (and V cleared)
If V is clear RO0, ..., ROo-1 must be copied to (the addresses in) the ith, ..., (i+o-1)th arguments; and if flags are required, they must be written to (the address in) the (i+o)th argument, and R cleared.

Definitions

Consider those variable registers V which will have to be preserved by this whole function: these are the ones that APCS requires to be preserved which we will be using ourselves, or which the SWI corrupts:

V = {4 < r < 10: there is an x such that r = Ix or r = Ox or r = Cx or r = Kx}

Also consider those registers A which are going to have to be saved over the call, because they contain addresses to be used for output. These are those of the ith, ..., (i+o+f-1)th arguments which are passed in R0, ..., R3:

A = {0 < r < 4: i < r < i+sup(inf(o+f,4)-i,0)}

Input

Very easy:

        ;keep current SP safe---it points at the tail of the argument list
        MOV     IP, SP
        ;save registers that need it over the call (SP -= 4*|V|)
        STMFD   SP!, {V}
        ;save registers that contain output addresses (SP -= 4*|A|)
        STMFD   SP!, {A}
        ;load registers for SWI that are in the argument tail
        LDM     IP, {I4, ..., I(i - 1)}
        ;load registers for SWI that are in registers already
        MOV     I3, A3
        MOV     I2, A2
        MOV     I1, A1
        MOV     I0, A0
        ;set up constants
        MOV     K(k - 1), whatever
        ...
        MOV     K0, whatever

(In fact, STMFD SP!, {V, A} will do rather than the two instructions above.)

After all this,

[SP, #4|A|+4|V|] contains saved arguments (as set up by APCS)
[SP, #4|A|] contains saved variables (saved by us)
[SP] contains saved output addresses (also saved by us where not already saved by APCS)

In fact, 2 variants are possible: first, if i < 4, we do not need IP before the SWI is called, so we can use it to save LR in. After the SWI, LR is corrupted anyway, so we can use it as we see fit. Second, if i > 4, we stack LR along with V, A. In the first case, on exit we have to do MOVS PC, IP (or MOV PC, IP for a 32-bit APCS). In the second, we just add PC to the list of registers to be restored on return.

Call the SWI

        ;do the call
        SWI     (swi)

Output

Harder, though the V-set case is easy:

        ;do error return
        BVS     exit

If V is clear, we have to retrieve the addresses passed in the (i+1)th, ..., (i+o-1)th arguments. These have been saved in 2 different places: argument i+x is at [SP, #4x] (i+x < 4), or at [SP, #4|V|+4|A|+4*(i+x-4)] (otherwise).

Initially, assume only that i > 4:

        ;get address for first argument
        LDR     IP, [SP, #4*|V| + 4|A| + 4*(i - 4)]
        STR     O0, [IP]

        ... repeat for other arguments ...

        ;get address for last argument
        LDR     IP, [SP, #4*|V| + 4|A| + 4*((i + o - 1) - 4)]
        STR     O(o - 1), [IP]

        ;if flags are required, do them too
        LDR     IP, [SP, #4*|V| + 4|A| + 4*((i + o) - 4)]
        STR     PC, [IP]

In fact, it may be the case that i+o+f-1 < 4. In this case, we have to replace any

        LDR     IP, [SP, #4*|A| + 4|S| + 4*((i + x) - 4)]
        STR     O(x), [IP]

sequences for which i+x < 4 by sequences that go

        LDR     IP, [SP, #4*x]
        STR     O(x), [IP]

where [SP, #4x] is the value that R(i+x) had on entry to the code, as stacked at the beginning.

If any of the output addresses were in registers that have been preserved, we do not need to reload them from memory. And we can supply an extra (very useful!) facility by checking for NULL pointers before writing to them.

There is one trick which is missed: we could use registers which are preserved by the call, but which we do not need to set to any particular value, to keep addresses in. This is left as an exercise for the reader. :-)

Return

Restore everthing to its initial state:

        MOV     R, #0
  exit
        ;skip over saved output addresses
        ADD     SP, SP, 4|A|
        ;restore saved variables
        LDMFD   SP!, {V}
        MOV     PC, LR

A piece of cake!

Type conversion for C

The DefMod file has its own set of types, which must be mapped to C types. The match is close, but DefMod allows structure types with arbitrarily repeated final members. These are not representable with standard C, but we use knowledge of how all ARM C compilers work to generate some macros that help with these.

To convert (CVT) a type from the DefMod form to the C form,

Int int
Short short
Byte unsigned char (is also byte)
Char char
Bits unsigned int (is also bits)
Bool int (is also osbool)
Asm asm_routine
String char
Data unsigned char (is also byte)
Ref type CVT(type) *
Struct (..., type, ...} struct {..., CVT(type), ...}
Union (..., type, ...} union {..., CVT(type), ...}
[const] type CVT(type) [VAL(const)]
id Convert_To_Extern (id)

and in writing prototypes

= type becomes CVT(type) on input, CVT(type) * on output
-> type becomes CVT(type) const * on input, CVT(type) ** on output
# num disappears
| type becomes CVT(type)
? disappears
FLAGS becomes unsigned * (output only)

Implementation notes

DefMod works by reading the interface file using a Yacc-generated parser derived and producing an internal form of the data in memory; it then emits this data in the required form. It is not written with speed or low memory-use in mind, but rather with robustness, correctness and ease of coding:

Robustness
Every error condition that might arise is checked for and reported to the user, including all filing system, i/o and SWI errors.
Correctness
If it works at all, it will produce the right answer. If it doesn't, it shows the error message to the user.
Ease of coding
2 shortcuts have simplified the structure of DefMod, at the expense of efficiency:
  1. There is no lexical analysis phase: lexical analysis is done by the same grammar that parses the interface file. This avoids the possibility that the lexical structure might interfere with the syntactical structure of the file.
  2. The parser uses string-valued variables for various purposes. Rather than construct some objects on the heap with malloc(), I have simply used a Yacc object big enough to hold a string of the largest possible length N. In combination with the point above, this means that building up a string of length l involves lN byte-copies.

These don't seem to be a problem in practice.

There is a small bootstrapping problem: DefMod is actually a client of OSLib. In order to build DefMod, there is therefore an implementation of the parts of OSLib that DefMod needs, as macros (using _swix()). This is called SWILib. In principal, these could now be replaced with the output of DefMod -l, but this would be to introduce a potentially lethal feedback loop.

The grammar

Yacc requires an input file containing the complete grammar with C code to apply at each reduction. Here we have only the grammar itself, with rules derived from the conventions above (see Syntax) deleted.

file: ws decl_SERIES_OPTION;

decl: title_decl | author_decl | needs_decl | const_decl | type_decl |
      swi_decl;

title_decl: TITLE ID DESCRIPTION_OPTION;

author_decl: AUTHOR DESCRIPTION;

needs_decl: NEEDS needs_LIST;

needs: ID;

const_decl: CONST const_defn_LIST;

const_defn: ID EQUALS type COLON const DESCRIPTION_OPTION;

const: NUM | ID;

type_decl: TYPE type_defn_LIST;

type_defn: ID DESCRIPTION_OPTION | ID EQUALS type DESCRIPTION_OPTION;

base_type: COLON ID
type: INT | SHORT | BYTE | CHAR | BITS | BOOL | REF type |
      STRING | ASM | DATA | STRUCT base_type_OPTION OPEN typed_var_LIST
      ellipsis_OPTION CLOSE | UNION OPEN toided_var_LIST CLOSE | SUB const
      BUS type | ID;

toid: type | VOID;

typed_var: type COLON ID DESCRIPTION_OPTION;

toided_var: toid COLON ID DESCRIPTION_OPTION;

swi_decl: SWI swi_defn_LIST;

swi_defn: ID EQUALS swi;

swi: OPEN number_part condition_part_OPTION CLOSE;

number_part: NUMBER NUM description_OPTION;

condition_part: entry_part | entry_part exit_part | entry_part
                absent_part | exit_part | absent_part;

entry_part: COMMA ENTRY OPEN entry_condition_LIST CLOSE;

exit_part: COMMA EXIT OPEN exit_condition_LIST CLOSE;

absent_part: COMMA ABSENT;

entry_condition: REG CONTAINS typed_var | REG REFERENCES typed_var |
                 REG CONSTANT NUM description_OPTION |
                 REG DISJOINS typed_var | REG CONJOINS typed_var |
                 REG ADDS typed_var | REG EXCLUSIVELY_DISJOINS typed_var
                 | FLAGS;

exit_condition: REG pling_OPTION CONTAINS typed_var |
                REG pling_OPTION REFERENCES typed_var | REG CORRUPTED |
                REG PLING | FLAGS pling_OPTION;

description: DESCRIPTION | STAR;

ws: ws_item_SEQUENCE_OPTION;

nl: '\n' | '\r';

space: spacechar | nl;

ws_item: space | comment;

comment: '/' '/' commentchar_SEQUENCE_OPTION nl;

TITLE:  t i t l e   ws;
AUTHOR: a u t h o r ws;
NEEDS:  n e e d s   ws;
CONST:  c o n s t   ws;
TYPE:   t y p e     ws;
SWI:    s w i       ws;

NUMBER: n u m b e r ws;
ENTRY:  e n t r y   ws;
EXIT:   e x i t     ws;
ABSENT: a b s e n t ws;

FLAGS:  f l a g s   ws;

CONTAINS:             '='     ws;
REFERENCES:           '-' '>' ws;
CONSTANT:             '#'     ws;
DISJOINS:             '|'     ws;
CONJOINS:             '&'     ws;
ADDS:                 '+'     ws;
EXCLUSIVELY_DISJOINS: '^'     ws;
CORRUPTED:            '?'     ws;
STAR:                 '*'     ws;
PLING:                '!'     ws;

INT:    '.' i n t       ws;
SHORT:  '.' s h o r t   ws;
BYTE:   '.' b y t e     ws;
CHAR:   '.' c h a r     ws;
BITS:   '.' b i t s     ws;
BOOL:   '.' b o o l     ws;
DATA:   '.' d a t a     ws;

VOID:   '.' v o i d     ws;
STRING: '.' s t r i n g ws;
ASM:    '.' a s m       ws;

STRUCT:   '.' s t r u c t ws;
UNION:    '.' u n i o n   ws;
REF:      '.' r e f       ws;
ELLIPSIS: '.' '.' '.'     ws;

SEMICOLON: ';' ws;
COMMA:     ',' ws;
EQUALS:    '=' ws;
OPEN:      '(' ws;
CLOSE:     ')' ws;
COLON:     ':' ws;
SUB:       '[' ws;
BUS:       ']' ws;

ID: id_start id_cont_SEQUENCE_OPTION ws;

NUM: num ws;

REG: r digit ws;

DESCRIPTION: '"' wordchar_SEQUENCE word_SEQUENCE_OPTION '"' ws;
word: space_SEQUENCE wordchar_SEQUENCE;
wordchar: simplechar | '\\' | '\'';

letter: a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p |
        q | r | s | t | u | v | w | x | y | z;
bit: '0' | '1';
digit: bit | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
hexit: digit | a | b | c | d | e | f;

id_start: letter;

id_cont: letter | digit | '_';

num: dec_num | hex_num | bin_num | char_num;

dec_num: digit_SEQUENCE | '-' digit_SEQUENCE;

hex_num: '0' x hexit_SEQUENCE | '&' hexit_SEQUENCE;

bin_num: '0' b bit_SEQUENCE | '%' bit_SEQUENCE;

char_num: '\'' numchars '\'';

numchars: numchar | numchar numchar | numchar numchar numchar |
          numchar numchar numchar numchar;

numchar: simplechar | compoundchar | spacechar | '"';
commentchar: simplechar | spacechar | '\\' | '"' | '\'';
spacechar: ' ' | '\xA0' | '\t';
simplechar: letter | digit | '!' | '#' | '$' | '%' | '&' | '(' | ')' |
            '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' |
            '>' | '?' | '@' | '[' | ']' | '^' | '_' | '`' | ' | '|' |
            '}' | '~' | '\x8C' | '\x8D' | '\x8E' | '\x8F' | '\x90' | 
            '\x91' | '\x92' | '\x93' | '\x94' | '\x95' | '\x96' |
            '\x97' | '\x98' | '\x99' | '\x9C' | '\x9D' | '\xA1' | 
            '\xA2' | '\xA3' | '\xA4' | '\xA5' | '\xA6' | '\xA7' |
            '\xA8' | '\xA9' | '\xAA' | '\xAB' | '\xAC' | '\xAD' | 
            '\xAE' | '\xAF' | '\xB0' | '\xB1' | '\xB9' | '\xB2' |
            '\xB3' | '\xB4' | '\xB5' | '\xB6' | '\xB7' | '\xB8' | 
            '\xBA' | '\xBB' | '\xBC' | '\xBD' | '\xBE' | '\xBF' |
            '\xD7' | '\xF7';

compoundchar: '\\' escapedchar | '\\' x hexit hexit;

escapedchar: '\'' | '"' | '\\' | n | '0';

An example file

TITLE ColourPicker;

NEEDS OS, Wimp;

CONST
   //Error numbers
   Error_ColourPickerUninit     = .Bits: &20D00,
   Error_ColourPickerBadModel   = .Bits: &20D01,
   Error_ColourPickerBadHandle  = .Bits: &20D02,
   Error_ColourPickerBadFlags   = .Bits: &20D03,
   Error_ColourPickerInUse      = .Bits: &20D04,
   Error_ColourPickerModelInUse = .Bits: &20D05,
   Error_ColourPickerBadReason  = .Bits: &20D06;

CONST
   //Colour model entry numbers
   ColourPicker_EntryDialogueStarting  = .Int: 0,
   ColourPicker_EntryDialogueFinishing = .Int: 1,
   ColourPicker_EntryRedrawArea        = .Int: 2,
   ColourPicker_EntryUpdateArea        = .Int: 3,
   ColourPicker_EntryReadValues        = .Int: 4,
   ColourPicker_EntrySetValues         = .Int: 5,
   ColourPicker_EntryProcessEvent      = .Int: 6,
   ColourPicker_EntrySetColour         = .Int: 7,
   //more here ...
   ColourPicker_EntryLimit             = .Int: 8;

TYPE
   ColourPicker_D,

   ColourPicker_Colour =
   .Struct
   (  OS_Colour: colour,
      .Int: size, //of the 'info' array
      .Int: info ...
   );

TYPE ColourPicker_DialogueFlags = .Bits;
CONST
   //Flag bits for ColourPicker_Dialogue
   ColourPicker_DialogueOffersTransparent = ColourPicker_DialogueFlags:    %1,
   ColourPicker_DialogueTransparent       = ColourPicker_DialogueFlags:   %10,
   ColourPicker_DialogueType              = ColourPicker_DialogueFlags: %1100,
   ColourPicker_DialogueTypeShift         = .Int: 2,
   ColourPicker_DialogueTypeNever         = .Bits: 0,
   ColourPicker_DialogueTypeClick         = .Bits: 1,
   ColourPicker_DialogueTypeClickDrag     = .Bits: 2,
   ColourPicker_DialogueIgnoreHelp        = ColourPicker_DialogueFlags: %10000,
   ColourPicker_DialogueIgnoreKeyPressed  = ColourPicker_DialogueFlags: %100000;
TYPE
   ColourPicker_Dialogue =
   .Struct
   (  ColourPicker_DialogueFlags: flags,
      .Ref .String: title,
      OS_Box: visible,
      .Int: xscroll,
      .Int: yscroll,
      OS_Colour: colour,
      .Int: size, //of the 'info' array
      .Int: info ...
   );

TYPE ColourPicker_ModelFlags = .Bits;
TYPE
   ColourPicker_Model =
   .Struct
   (  ColourPicker_ModelFlags: flags,
      .Ref .String: name,
      .Ref .String: description,
      .Int: info_size,
      OS_Coord: pane_size,
      [ColourPicker_EntryLimit] .Ref .Asm: entries
   );

CONST
   //Colour model numbers (for 'size')
   ColourPicker_ModelSizeRGB  = .Int: 16,
   ColourPicker_ModelSizeCMYK = .Int: 20,
   ColourPicker_ModelSizeHSV  = .Int: 16;

CONST
   //Colour model numbers (for 'info + 0')
   ColourPicker_ModelRGB  = .Int: 0,
   ColourPicker_ModelCMYK = .Int: 1,
   ColourPicker_ModelHSV  = .Int: 2;

CONST
   //WIMP message numbers
   Message_ColourPickerColourChoice         = .Bits: &47700,
   Message_ColourPickerColourChanged        = .Bits: &47701,
   Message_ColourPickerCloseDialogueRequest = .Bits: &47702,
   Message_ColourPickerOpenParentRequest    = .Bits: &47703,
   Message_ColourPickerResetColourRequest   = .Bits: &47704;

CONST
   //for the various messages
   ColourPicker_ColourTransparent = ColourPicker_ColourFlags: %1,
   ColourPicker_ColourDragging    = ColourPicker_ColourFlags: %10;

TYPE
   //Types for message blocks
   ColourPicker_MessageColourChoice =
      .Struct
      (  ColourPicker_D: d,
         ColourPicker_ColourFlags: flags,
         OS_Colour: colour,
         .Int: size, //of the 'info' array (in bytes !)
         .Int: info ...
      ),

   ColourPicker_MessageColourChanged =
      .Struct
      (  ColourPicker_D: d,
         ColourPicker_ColourFlags: flags,
         OS_Colour: colour,
         .Int: size, //of the 'info' array
         .Int: info ...
      ),

   ColourPicker_MessageOpenParentRequest =
      .Struct
      (  ColourPicker_D: d
      ),

   ColourPicker_MessageCloseDialogueRequest =
      .Struct
      (  ColourPicker_D: d
      ),

   ColourPicker_MessageResetColourRequest =
      .Struct
      (  ColourPicker_D: d
      );

SWI
   ColourPicker_RegisterModel =
   (  NUMBER &47700 "For internal use only",
      ENTRY
      (  R0 = .Int: model_no,
         R1 -> ColourPicker_Model: model,
         R2 = .Ref Void: workspace
   )  ),

   ColourPicker_DeregisterModel =
   (  NUMBER &47701 "For internal use only",
      ENTRY
      (  R0 = .Int: model_no
   )  );

TYPE ColourPicker_OpenFlags = .Bits;
CONST
   //for ColourPicker_OpenDialogue
   ColourPicker_OpenTransient = ColourPicker_OpenFlags: %1,
   ColourPicker_OpenSubMenu   = ColourPicker_OpenFlags: %10, //only relevant if Transient
   ColourPicker_OpenToolbox   = ColourPicker_OpenFlags: %10; //only relevant if Permanent
SWI
   ColourPicker_OpenDialogue =
   (  NUMBER &47702 "Creates and opens a colour picker dialogue",
      ENTRY
      (  R0 = ColourPicker_OpenFlags: flags,
         R1 -> ColourPicker_Dialogue: dialogue
      ),
      EXIT
      (  R0! = ColourPicker_D: d,
         R1 = Wimp_W: w
   )  );

TYPE ColourPicker_CloseFlags = .Bits;
SWI ColourPicker_CloseDialogue =
   (  NUMBER &47703 "Closes a colour picker dialogue which is in
            progress",
      ENTRY
      (  R0 = ColourPicker_CloseFlags: flags,
         R1 = ColourPicker_D: d
   )  );

TYPE
   ColourPicker_UpdateFlags = .Bits;
CONST
   //for ColourPicker_UpdateDialogue
   ColourPicker_UpdateOffersTransparent = ColourPicker_UpdateFlags: %1,
   ColourPicker_UpdateTransparent       = ColourPicker_UpdateFlags: %10,
   ColourPicker_UpdateType              = ColourPicker_UpdateFlags: %100,
   ColourPicker_UpdateVisible           = ColourPicker_UpdateFlags: %1000,
   ColourPicker_UpdateScroll            = ColourPicker_UpdateFlags: %10000,
   ColourPicker_UpdateTitle             = ColourPicker_UpdateFlags: %100000,
   ColourPicker_UpdateColour            = ColourPicker_UpdateFlags: %1000000,
   ColourPicker_UpdateModel             = ColourPicker_UpdateFlags: %10000000,
   ColourPicker_UpdateIgnoreHelp        = ColourPicker_UpdateFlags: %100000000,
   ColourPicker_UpdateIgnoreKeyPressed  = ColourPicker_UpdateFlags: %1000000000;
SWI
   ColourPicker_UpdateDialogue =
   (  NUMBER &47704 "Updates some or all of the contents of a colour
            picker dialogue",
      ENTRY
      (  R0 = ColourPicker_UpdateFlags: flags,
         R1 = ColourPicker_D: d,
         R2 -> ColourPicker_Dialogue: dialogue
   )  );

TYPE ColourPicker_ReadFlags = .Bits;
SWI
   ColourPicker_ReadDialogue =
   (  NUMBER &47705 "Reads the current state of a colour picker
dialogue
            without changing it",
      ENTRY
      (  R0 = ColourPicker_ReadFlags: flags,
         R1 = ColourPicker_D: d,
         R2 = .Ref ColourPicker_Dialogue: dialogue
      ),
      EXIT
      (  R1 = Wimp_W: w,
         R2 = .Int: size
   )  );

TYPE ColourPicker_SetFlags = .Bits;
SWI
   ColourPicker_SetColour =
   (  NUMBER &47706 "Reserved for future expansion",
      ENTRY
      (  R0 = ColourPicker_SetFlags: flags,
         R1 -> ColourPicker_Colour: colour
   )  );

TYPE ColourPicker_HelpFlags = .Bits;
SWI
   ColourPicker_HelpReply =
   (  NUMBER &47707 "Makes a colour picker respond to a
            Message_HelpRequest with its own help text",
      ENTRY
      (  R0 = ColourPicker_HelpFlags: flags,
         R1 -> Wimp_Message: help_request
   )  );

SWI
   ColourPicker_ModelSWI = (NUMBER &47708 "For internal use only",
         ABSENT),

   ColourPickerModelSWI_ColourChanged =
   (  NUMBER &47708,
      ENTRY
      (  R0 # 0 "Informs the front end to send a message to the client,
               if required",
         R1 -> ColourPicker_Colour: colour
   )  ),

   //Cause the Picker to send a message to the application. if required.
   ColourPickerModelSWI_ColourChangedByDragging =
   (  NUMBER &47708,
      ENTRY
      (  R0 # 1 "Informs the front end to send a dragging message to the
               client, if required",
         R1 -> ColourPicker_Colour: colour
   )  ),

   ColourPickerModelSWI_ClaimEvent =
   (  NUMBER &47708,
      ENTRY
      (  R0 # 2 "Informs the front end that the back end wants an event
               type",
         R1 = .Int: event,
         R2 -> ColourPicker_Colour: colour
   )  ),

   ColourPickerModelSWI_ReleaseEvent =
   (  NUMBER &47708,
      ENTRY
      (  R0 # 3 "Informs the front end that the back end no longer wants an
            event type",
         R1 = .Int: event,
         R2 -> ColourPicker_Colour: colour
   )  ),

   ColourPickerModelSWI_ProcessKey =
   (  NUMBER &47708,
      ENTRY
      (  R0 # 4 "Passes an unhandled key press on to the front end",
         R1 = .Int: c,
         R2 -> ColourPicker_Colour: colour
   )  );

SWI
   Service_ColourPickerLoaded =
   (  NUMBER 0x30,
      ENTRY
      (  R1 # &93 "For internal use only",
         R2 -> .Asm: loaded_service,
         R3 = .Ref Void: workspace
   )  )

Future possibilities

Could have an UPDATE category, optional between ENTRY and EXIT categories. This would allow 1 argument to be saved on calls that use a value and then update it.

Could have more knowledge about buffers and buffer lengths, and provide a more consistent interface. (SWI's that fill buffers have two arguments, usually called buffer and size, and may return one of: a pointer to the next free byte (called end); a number of bytes written (called used); or a count of buffer bytes not used (called spare). More consistency could be derived by having a cleverer veneer.

Alternatively (or additionally), these calls couild always return the pointer to the filled-in buffer, so you could nest calls as with C string functions.

It would be good to have support for enum's, so you could have cross-references generated in the help files from the type name to allowable values of that type. We could also have support for sets of enumerated types.

Some SWI's have two or more registers that are equally likely to be needed as results. Could they be persuaded to return a structure-valued result? Under C release 5, it's easy for the non-x case, but it would have to work for all cases to be useful.

More info in C help (register <=> arg mappings, other notes).

Put the text for x and non-x forms into the same help page, indexed by both words.

Modify everything in sight so that when an array is sized by a constant, the manifest makes it through to the help text, highlighted so you can find its value.

Use registers that are otherwise unused by the veneer code as an alternative to the stack in order to save on memory accesses.

Revision history

1st Jul 1992 J R C Started.
5th Jul 1992 J R C Added Handling of FLAGS arguments, handling of constant arguments, handling of SWI's that corrupt random registers.
6th Jul 1992 J R C Changed the way reason codes are handled, implemented them.
7th Jul 1992 J R C Save LR over calls, in case we are in SVC mode.
10th Jul 1992 J R C Added type conversion notes.
J R C Handling of BLOCK arguments. (If there is no EXIT clause, and the last ENTRY argument is of the form Rx -> .Struct ... then the constructed interface assumes that the structure is passed by value, i e, its fields are passed in order. See wimp_set_icon_state() for an instance of this.)
24th Oct 1994 J R C Updated for release to those that ask.
20th Jul 1997 J R C Updated for general release on http://www.doves.demon.co.uk.
26th Mar 1999 T V Amended definition of conversion types for String and Data for Free OSLib 5.4.
4th Mar 2000 T J H Added base types for structures to the grammar definition.